package parser

import (
	"archive/zip"
	"bytes"
	"context"
	_ "embed"
	"github.com/pkg/errors"
	"go.uber.org/multierr"
	"io"
	"os"
	"path/filepath"
	"strings"
	"text/template"
)

//go:embed router.gogo
var routerTemplate string

func (prj Project) WriteGo(w io.Writer) error {
	return template.Must(template.New("").Funcs(template.FuncMap{
		"title":    strings.Title,
		"trim":     strings.TrimSpace,
		"lower":    strings.ToLower,
		"upper":    strings.ToUpper,
		"contains": strings.Contains,
	}).Parse(routerTemplate)).Execute(w, prj)
}

//go:generate curl -SsL -o app.zip $APP_ZIP_DOWNLOAD_URL
//go:embed app.zip
var embedApp []byte

func ExtraApp(ctx context.Context, dir string, itemEvent func(file *zip.File, err error) error) error {
	zr, err := zip.NewReader(bytes.NewReader(embedApp), int64(len(embedApp)))
	if err != nil {
		return errors.WithStack(err)
	}

	if ctx.Err() != nil {
		return errors.WithStack(err)
	}

	dir = filepath.FromSlash(dir)
	for _, file := range zr.File {
		if ctx.Err() != nil {
			return errors.WithStack(err)
		}
		if err := unZip(file, dir); err != nil {
			if itemEvent != nil {
				if err := itemEvent(file, err); err != nil {
					return errors.WithStack(err)
				}
			}
			err = multierr.Append(err, err)
		}
	}

	return errors.WithStack(err)
}

func unZip(file *zip.File, dir string) error {
	path := filepath.Join(dir, filepath.FromSlash(strings.TrimPrefix(strings.Trim(filepath.ToSlash(file.Name), "/"), "app/")))
	fi := file.FileInfo()

	if fi.IsDir() {
		return os.MkdirAll(path, fi.Mode())
	}

	if err := os.MkdirAll(filepath.Dir(path), os.ModePerm); err != nil {
		return err
	}

	src, err := file.Open()
	if err != nil {
		return err
	}
	defer src.Close()

	tempFn := path + ".unzip.tmp"
	dst, err := os.Create(tempFn)
	if err != nil {
		return err
	}
	defer dst.Close()

	if _, err := io.Copy(dst, src); err != nil {
		_ = os.RemoveAll(tempFn)
		return err
	}

	if err := os.Rename(path+".unzip.tmp", path); err != nil {
		_ = os.RemoveAll(tempFn)
		return err
	}
	return nil
}
